/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package main.EntityClassGraph;

import java.io.Serializable;
import java.util.*;
import main.KBProcess.Element;
import main.KBProcess.Element.ElementType;
import main.KBProcess.KnowledgeBaseDocument;
import main.Ontology.ElementOntologySet;
import org.jgrapht.graph.DefaultDirectedWeightedGraph;

/**
 *
 * @author Isaac Osesina
 */
public class ElementOntologySetGraph  extends DefaultDirectedWeightedGraph<ElementOntologySetVertex, EntityClassGraphEdge> implements Serializable, Cloneable {

    public static enum EdgeStatistics{MODE, MEAN, MIN};
    
    public ElementOntologySetGraph() {
        super(EntityClassGraphEdge.class);
    }

    
    public void addVertex(Element element) {
        if(element==null || element.getType() != ElementType.ENTITY)
            return;
        
        ElementOntologySetVertex currentVertex = this.getVertex(element.getOntologies());
        boolean addVertex = false;
        if(currentVertex==null || !currentVertex.containsElement(element)) {
            currentVertex = currentVertex==null ? new ElementOntologySetVertex(element.getOntologies()): currentVertex;
            addVertex = addVertex(currentVertex, element);
        }
        if(addVertex)
            addEdge(element);
    }
    
    private void addEdge(Element element) {
        if(element==null || element.getType() != ElementType.ENTITY)
            return;
        
        ElementOntologySetVertex currentVertex = this.getVertex(element.getOntologies());
        if(currentVertex == null)
            return;
        
        /**Adding edges between the current vertex and both the PreVertex and PostVertex may create double counted edges.
        If fragments are added sequentially, it is enough to create a link between the current vertex and PreVertex
        */
        Element preEntity = element.getPreElement() == null ? null:element.getPreElement().getPreElement();
        if(preEntity != null && preEntity.getOntologies() != null) {
            ElementOntologySetVertex preVertex = this.getVertex(preEntity.getOntologies());
            boolean addVertex = true;
            if(preVertex == null || !preVertex.containsElement(preEntity)) {
                preVertex = preVertex==null ? new ElementOntologySetVertex(preEntity.getOntologies()):preVertex;
                addVertex = addVertex(preVertex, preEntity);
            }
            if(addVertex) {
                EntityClassGraphEdge edge = this.getEdge(preVertex, currentVertex);
                if(edge == null){
                    edge = new EntityClassGraphEdge(preEntity.getKnowledgeBaseDocument());
                    this.addEdge(preVertex, currentVertex, edge);
                }
                else {
                    setEdgeWeight(edge, edge.incrementWeight());
                    edge.addKnowledgeBaseDocument(preEntity.getKnowledgeBaseDocument());
                }
            }
        }
        
        
        Element postEntity = element.getPostElement() == null ? null:element.getPostElement().getPostElement();
        if(postEntity != null && postEntity.getOntologies() != null) {        
            ElementOntologySetVertex postVertex = this.getVertex(postEntity.getOntologies());
            boolean addVertex = true;
            if(postVertex == null || !postVertex.containsElement(postEntity)) {
                postVertex = postVertex==null ? new ElementOntologySetVertex(postEntity.getOntologies()):postVertex;
                addVertex = addVertex(postVertex, postEntity);
            }
            if(addVertex) {
                EntityClassGraphEdge edge = this.getEdge(currentVertex, postVertex);
                if(edge == null) {
                    edge = new EntityClassGraphEdge(postEntity.getKnowledgeBaseDocument());
                    this.addEdge(currentVertex, postVertex, edge);
                }
                else {
                    setEdgeWeight(edge, edge.incrementWeight());
                    edge.addKnowledgeBaseDocument(postEntity.getKnowledgeBaseDocument());
                }
            }
        }
    }
    
    public boolean addVertex(ElementOntologySetVertex v, Element e) {
        if(v==null || v.containsElement(e))
            return false;
                
        v.addElement(e);
        this.addVertex(v);
        return true;
    }
    
    public ElementOntologySetVertex getVertex(ElementOntologySet entityClass) {
        if(entityClass == null || entityClass.isEmpty())
            return null;
        
        for(ElementOntologySetVertex v:vertexSet()) {
            if(v.equals(entityClass)) {
                return v;
            }
        }

        return null;
    }
    
    
    public Set<EntityClassGraphEdge> getAllEdge(ElementOntologySet v1, ElementOntologySet v2) {
        if(this.getVertex(v1)==null || this.getVertex(v2)==null)
            return null;
        return this.getAllEdges(getVertex(v1), getVertex(v2));
    }
    
    
    public Map<KnowledgeBaseDocument, Double> getEdgeDistributionDocument(ElementOntologySetVertex v1, ElementOntologySetVertex v2) {
        Set<EntityClassGraphEdge> allEdges = this.getAllEdge(v1, v2);
        if(allEdges == null || allEdges.isEmpty())
            return null;
        
        Map<KnowledgeBaseDocument, Double> m = new HashMap<KnowledgeBaseDocument, Double>();
        
        for(EntityClassGraphEdge e:allEdges) {
            List<KnowledgeBaseDocument> list = e.getKnowledgeBaseDocumentSet();
            if(list == null)
                continue;
            for(KnowledgeBaseDocument k:list) {
                Double count = m.get(k);
                if(count == null) {
                    m.put(k, EntityClassGraphEdge.increment);
                }
                else {
                    m.put(k, count + EntityClassGraphEdge.increment);
                }
            }
        }
        return m;
    }
    
    public Double getEdgeBy(ElementOntologySetVertex v1, ElementOntologySetVertex v2, EdgeStatistics edgeStatistics) {
        if(v1==null || v2==null)
            return null;
        
        Map<KnowledgeBaseDocument, Double> edgeBy = getEdgeDistributionDocument(v1, v2);
        if(edgeBy == null) {
            return null;
        }
        switch(edgeStatistics) {
            case MODE:
                double max = 0;
                for(KnowledgeBaseDocument e:edgeBy.keySet()) {
                    if(edgeBy.get(e) > max)
                        max = edgeBy.get(e);
                }                
                return max;
            case MEAN:
                double median = 0;
                for(KnowledgeBaseDocument e:edgeBy.keySet()) {
                    median += edgeBy.get(e);
                }
                return median/edgeBy.size();
            case MIN:
                double min = Double.MAX_VALUE;
                for(KnowledgeBaseDocument e:edgeBy.keySet()) {
                    if(min > edgeBy.get(e))
                        min = edgeBy.get(e);
                }
                return min==Double.MAX_VALUE ? null:min;
        }
        
        return null;
    }
    
    public Double getEdgeBy(ElementOntologySet v1, ElementOntologySet v2, EdgeStatistics edgeStatistics) {
        return getEdgeBy(getVertex(v1), getVertex(v2), edgeStatistics);
    }
    
    
    public Set<EntityClassGraphEdge> getEdge(ElementOntologySet v1, ElementOntologySet v2) {
        ElementOntologySetVertex vertex = getVertex(v1);
        ElementOntologySetVertex vertex1 = getVertex(v2);
        if(vertex == null || vertex1 == null)
            return null;
        else 
            return this.getAllEdges(vertex, vertex1);
    }
    
    
}
